package main

import (
	pb "ihos/ihos"
	"log"
	"sort"
	"time"
)

const PAIDUI_TIMEOUT_IN_SECS = (10)

type HouZhenPaiDuis struct {
	paiban int32
	pds    []*houzhen_paidui
}

func (gs *HouZhenPaiDuis) Len() int { return len(gs.pds) }
func (gs *HouZhenPaiDuis) Less(i, j int) bool {
	return gs.pds[i].paihao < gs.pds[j].paihao
}
func (gs *HouZhenPaiDuis) Swap(i, j int) {
	gs.pds[i], gs.pds[j] = gs.pds[j], gs.pds[i]
}

type ActiveShiPinYongHu struct {
	syncTime int64
	yongHu   *pb.ShiPinYongHuL5
}
type MenZhenPaiDui struct {
	//active ShiPinYongHu
	yhs map[int32]*ActiveShiPinYongHu
	//to calculate number JiaoHao in front of, need cache something
	pd []*HouZhenPaiDuis //cache of table-houzhen_paidui, group by menzhen paiban
}

func (d *MenZhenPaiDui) CacheHouZhenPaiDui(h *houzhen_paidui) {
	log.Println("CacheHouZhenPaiDui:", h)
	for i := 0; i < len(d.pd); i++ {
		if d.pd[i].paiban == h.paiban {
			//paiban exist
			for j := 0; j < len(d.pd[i].pds); j++ {
				if d.pd[i].pds[j].id == h.id {
					//found, then repace
					d.pd[i].pds[j] = h
					sort.Sort(d.pd[i])
					return
				}
			}
			//not found, add it
			d.pd[i].pds = append(d.pd[i].pds, h)
			sort.Sort(d.pd[i])
			return
		}
	}
	//paiban not exist, create and cache
	p := &HouZhenPaiDuis{
		paiban: h.paiban,
		pds:    make([]*houzhen_paidui, 0),
	}
	p.pds = append(p.pds, h)
	d.pd = append(d.pd, p)
}

func (d *MenZhenPaiDui) CalRemain() {
	for i := 0; i < len(d.pd); i++ {
		r := int32(0)
		for j := 0; j < len(d.pd[i].pds); j++ {
			d.pd[i].pds[j].remain = r
			if len(d.pd[i].pds[j].yonghu) > 0 {
				r++
			}
		}
	}
}

func (d *MenZhenPaiDui) GetRemain(hid int32) int32 {
	for i := 0; i < len(d.pd); i++ {
		for j := 0; j < len(d.pd[i].pds); j++ {
			if d.pd[i].pds[j].id == hid {
				return d.pd[i].pds[j].remain
			}
		}
	}
	//log.Println("ERROR:Cannot found paidui 1:", hid)
	return 255
}

func (d *MenZhenPaiDui) GetRemainByHuaHao(gid int32) (r, o, t int32) {
	for i := 0; i < len(d.pd); i++ {
		for j := 0; j < len(d.pd[i].pds); j++ {
			if d.pd[i].pds[j].guahao == gid {
				return d.pd[i].pds[j].remain, int32(j + 1), int32(len(d.pd[i].pds))
			}
		}
	}
	//log.Println("ERROR:Cannot found paidui 2:", gid)
	return 255, 255, 255
}

func (d *MenZhenPaiDui) Online(in *pb.ShiPinYongHuL5) {
	log.Println("Online:")
	//ShiPin YongHu be online, update to table-houzhen_paidui/yonghu
	if in.GetSPXX() == nil {
		return
	}
	//only process in houzhen/menzhen room
	if in.GetSPXX().GetLX() != pb.SPLX_HOUZHEN &&
		in.GetSPXX().GetLX() != pb.SPLX_WENZHEN {
		return
	}
	//loop all guahaos
	for _, v := range in.GetXGGHS() {
		d.yhs[in.GetID()] = &ActiveShiPinYongHu{
			syncTime: time.Now().Unix(),
			yongHu:   in,
		}
		if in.GetONLINE() == false {
			in.ONLINE = true
			s := new(shipin_yonghu).fromPB(in)
			s.UpdateOnline()
		}

		hz := &houzhen_paidui{
			riqi:   GetNowHx(0),
			guahao: v.GetID(),
		}

		hs, err := hz.GetPaiDuiList()
		if err != nil {
			log.Println(err)
			return
		}

		// log.Println("guahao paiduis:", hs)

		for _, h := range hs {
			// log.Println("houzhen:", h)
			found := false
			for _, y := range h.yonghu {
				//YongHu Already In PaiDui
				if in.GetID() == int32(y) {
					found = true
					break
				}
				//todo: check if existing ShiPinYongHu is same
			}
			if found {
				continue
			}
			h.yonghu = append(h.yonghu, int64(in.GetID()))
			if err := h.UpdateYongHu(); err != nil {
				log.Println(err)
				continue
			}
			//reset paidui STATUS when online
			h.status = 0
			if err := h.UpdateStatus(); err != nil {
				log.Println(err)
				continue
			}
			d.CacheHouZhenPaiDui(h)
		}

		d.CalRemain()
	}
}

func (d *MenZhenPaiDui) Offline(in *pb.ShiPinYongHuL5) {
	log.Println("Offline:")
	//ShiPin YongHu be offline, remove from table-houzhen_paidui/yonghu
	for _, v := range in.GetXGGHS() {
		hz := &houzhen_paidui{
			riqi:   GetNowHx(0),
			guahao: v.GetID(),
		}
		hs, err := hz.GetPaiDuiList()
		if err != nil {
			log.Println(err)
			return
		}

		for _, h := range hs {
			for i := 0; i < len(h.yonghu); i++ {
				if in.GetID() == int32(h.yonghu[i]) {
					h.yonghu = append(h.yonghu[:i], h.yonghu[i+1:]...)
					i--
				}
			}
			if err := h.UpdateYongHu(); err != nil {
				log.Println(err)
				return
			}
			d.CacheHouZhenPaiDui(h)
		}

		d.CalRemain()
		if _, present := d.yhs[in.GetID()]; present == true {
			delete(d.yhs, in.GetID())
			if in.GetONLINE() == true {
				in.ONLINE = false
				s := new(shipin_yonghu).fromPB(in)
				s.UpdateOnline()
			}
		}
	}
}

func (d *MenZhenPaiDui) AddGuaHaoToPaiDui(in *pb.GuaHao) {
	if in.GetISVALID() == false {
		return
	}

	// log.Println("AddGuaHaoToPaiDui:", in)

	//Get GuaHao's PaiBan
	m := &menzhen_paiban{
		hoscode: in.GetHOSCODE(),
		//deptcode:   in.GetDEPTCODE(),
		//doctorcode: in.GetDOCTORCODE(),
		endtime: time.Now().Unix(),
	}

	mls, err := m.GetMenZhenPaiBanList()
	if err != nil || len(mls) == 0 {
		log.Println("Get PaiBan Failed")
		return
	}

	for _, v := range mls {
		// log.Println("paiban:", k, v)
		switch pb.MenZhenPaiBan_PaiBanLeiXing(v.leixing) {
		case pb.MenZhenPaiBan_PUTONG:
			if in.GetDEPTCODE() == v.deptcode {
				d.AddGuaHaoToMenZhenPaiDui(in, v.toPB())
			}
		case pb.MenZhenPaiBan_ZHUANJIA:
			if in.GetDEPTCODE() == v.deptcode && in.GetDOCTORCODE() == v.doctorcode {
				d.AddGuaHaoToMenZhenPaiDui(in, v.toPB())
			}
		case pb.MenZhenPaiBan_QUANYUAN:
			d.AddGuaHaoToMenZhenPaiDui(in, v.toPB())
		}
	}
}

func (d *MenZhenPaiDui) AddGuaHaoToMenZhenPaiDui(in *pb.GuaHao, paiban *pb.MenZhenPaiBan) {
	if in.GetISVALID() == false {
		return
	}

	if paiban == nil {
		return
	}

	log.Println("AddGuaHaoToMenZhenPaiDui:", in.GetID(), paiban.GetID())

	//Check if GuaHao already in PaiDui
	h := &houzhen_paidui{
		paiban: paiban.GetID(),
		riqi:   GetNowHx(0),
		guahao: in.GetID(),
	}
	if err := h.Get(); err != nil {
		//GuaHao not in Paidui, cal paidui number and save
		if err := h.GetPaiHaoLast(); err != nil {
			h.paihao = 1
		} else {
			h.paihao++
		}
		h.SaveToDB(0)
	} else {
		//add back online ShiPinYongHu
		for i := 0; i < len(h.yonghu); i++ {
			s := &shipin_yonghu{id: int32(h.yonghu[i])}
			if err := s.GetFromDB(s.id); err == nil && s.online == true {
				//online shipin yonghu
				d.yhs[s.toPB().GetID()] = &ActiveShiPinYongHu{
					syncTime: time.Now().Unix(),
					yongHu:   s.toPB(),
				}
			} else {
				h.yonghu = append(h.yonghu[:i], h.yonghu[i+1:]...)
				i--
			}
		}
		// log.Println(h)
		h.SaveToDB(h.id)
	}
	//cache houzhen paidui
	d.CacheHouZhenPaiDui(h)
}

func (d *MenZhenPaiDui) CreatePaiDui(in *menzhen_paiban) {
	gh := &guahao{
		hoscode:    in.hoscode,
		deptcode:   in.deptcode,
		doctorcode: in.doctorcode,
	}
	ghs, err := gh.GetGuaHaoList()
	if err != nil {
		log.Println(err)
		return
	}
	for _, g := range ghs {
		d.AddGuaHaoToMenZhenPaiDui(g.toPB(), in.toPB())
	}
}

func (d *MenZhenPaiDui) Init() {
	p := &menzhen_paiban{
		endtime: time.Now().Unix(),
	}
	pbs, err := p.GetMenZhenPaiBanList()
	if err != nil {
		log.Println(err)
		return
	}
	for _, v := range pbs {
		d.CreatePaiDui(v)
	}
}

func (d *MenZhenPaiDui) DeInit() {
	d.yhs = make(map[int32]*ActiveShiPinYongHu, 0)
	d.pd = make([]*HouZhenPaiDuis, 0)
}

var menZhenPaiDui = &MenZhenPaiDui{
	yhs: make(map[int32]*ActiveShiPinYongHu, 0),
	pd:  make([]*HouZhenPaiDuis, 0),
}

func HouZhenPaiDuiInit() {
	go func() {
		for {
			day := time.Now().Day()
			menZhenPaiDui.Init()
			for {
				time.Sleep(5 * time.Second)
				log.Println("Scan active user list...")
				for _, v := range menZhenPaiDui.yhs {
					// log.Println(k, v)
					//check if any ShiPinYongHu active timeout
					if v.syncTime+PAIDUI_TIMEOUT_IN_SECS < time.Now().Unix() {
						menZhenPaiDui.Offline(v.yongHu)
					}
				}
				//dump paidui
				// for k, v := range menZhenPaiDui.pd {
				// 	log.Println("MenZhen:", k, v)
				// 	for m, n := range v.pds {
				// 		log.Println("PaiDui:", m, n, n.remain)
				// 	}
				// }
				//check if tomorrow is arrived
				//offline may not find record after 00:00
				// if day != time.Now().Day() && len(menZhenPaiDui.yhs) == 0 {
				if day != time.Now().Day() {
					log.Println("Tomorrow Arrived, re-create paidui")
					break
				}
			}
			menZhenPaiDui.DeInit()
		}
	}()
}
